/** \file    write.cpp
  * \brief   Printing & writing of xml stream.
  * \date    2003
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/xml/spirit/src/write.cpp,v 1.2 2004/03/24 06:23:13 cscom1r4 Exp $
  */

#include "xml/spirit.h"
#include "spirit.inl"
#include <fstream>
#include <algorithm>
#include <functional>

namespace xml {

struct write_char {
	std::ostream & s;
	write_char(std::ostream & stream) : s(stream) {}
	void operator () (char c) {
		switch (c) {
		case '"':
			s << "&quot;";
			break;
		case '&':
			s << "&amp;";
			break;
		case '<':
			s << "&lt;";
			break;
		default:
			s << c;
		}
	}
};

struct write_attr {
	std::ostream & s;
	write_attr(std::ostream & stream) : s(stream) {}
	void operator () (const spirit::attrs_type::value_type & t) {
		s << ' ' << t.first << "=\"";
		std::for_each(t.second.begin(), t.second.end(), write_char(s));
		s << '"'; 
	}
};

class write_element : public std::binary_function<const spirit::element *, int, void> {
	std::ostream & s;
public:
	write_element(std::ostream & stream) : s(stream) {}
	void operator () (const spirit::element * e, int indent = 0) const {
		using namespace std;
		s << string(indent * 2, ' ') << '<' << e->tag();
		for_each(e->_attrs.begin(), e->_attrs.end(), write_attr(s));
		if (e->_children.empty())
			s << "/>\n";
		else {
			s << ">\n";
			for_each(e->_children.begin(), e->_children.end(),
				bind2nd(*this, indent+1));
			s << string(indent * 2, ' ') << "</" << e->tag() << ">\n";
		}
	}
};

void spirit::write(std::ostream & s)
{
	assert(_root);
	s << "<?xml version=\"1.0\" ?>\n";
	(write_element(s))(_root);
}

void spirit::write(const string & filename)
{
	std::ofstream out;
	out.exceptions(std::ios::badbit | std::ios::failbit);
	out.open(filename.c_str(), std::ios::binary);
	write(out);
}

} //namespace xml

